<?php

/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Gdata
 * @subpackage Gdata
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @version    $Id: AuthSub.php 24319 2011-07-30 13:43:41Z mikaelkael $
 */

/**
 * Zend_Gdata_HttpClient
 */
require_once 'Zend/Gdata/HttpClient.php';

/**
 * Zend_Version
 */
require_once 'Zend/Version.php';

/**
 * Wrapper around Zend_Http_Client to facilitate Google's "Account Authentication
 * Proxy for Web-Based Applications".
 *
 * @see http://code.google.com/apis/accounts/AuthForWebApps.html
 *
 * @category   Zend
 * @package    Zend_Gdata
 * @subpackage Gdata
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Gdata_AuthSub {
	
	const AUTHSUB_REQUEST_URI = 'https://www.google.com/accounts/AuthSubRequest';
	
	const AUTHSUB_SESSION_TOKEN_URI = 'https://www.google.com/accounts/AuthSubSessionToken';
	
	const AUTHSUB_REVOKE_TOKEN_URI = 'https://www.google.com/accounts/AuthSubRevokeToken';
	
	const AUTHSUB_TOKEN_INFO_URI = 'https://www.google.com/accounts/AuthSubTokenInfo';
	
	/**
	 * Creates a URI to request a single-use AuthSub token.
	 *
	 * @param string $next (required) URL identifying the service to be
	 * accessed.
	 * The resulting token will enable access to the specified service only.
	 * Some services may limit scope further, such as read-only access.
	 * @param string $scope (required) URL identifying the service to be
	 * accessed.  The resulting token will enable
	 * access to the specified service only.
	 * Some services may limit scope further, such
	 * as read-only access.
	 * @param int $secure (optional) Boolean flag indicating whether the
	 * authentication transaction should issue a secure
	 * token (1) or a non-secure token (0). Secure tokens
	 * are available to registered applications only.
	 * @param int $session (optional) Boolean flag indicating whether
	 * the one-time-use  token may be exchanged for
	 * a session token (1) or not (0).
	 * @param string $request_uri (optional) URI to which to direct the
	 * authentication request.
	 */
	public static function getAuthSubTokenUri($next, $scope, $secure = 0, $session = 0, $request_uri = self::AUTHSUB_REQUEST_URI) {
		$querystring = '?next=' . urlencode ( $next ) . '&scope=' . urldecode ( $scope ) . '&secure=' . urlencode ( $secure ) . '&session=' . urlencode ( $session );
		return $request_uri . $querystring;
	}
	
	/**
	 * Upgrades a single use token to a session token
	 *
	 * @param string $token The single use token which is to be upgraded
	 * @param Zend_Http_Client $client (optional) HTTP client to use to
	 * make the request
	 * @param string $request_uri (optional) URI to which to direct
	 * the session token upgrade
	 * @return string The upgraded token value
	 * @throws Zend_Gdata_App_AuthException
	 * @throws Zend_Gdata_App_HttpException
	 */
	public static function getAuthSubSessionToken($token, $client = null, $request_uri = self::AUTHSUB_SESSION_TOKEN_URI) {
		$client = self::getHttpClient ( $token, $client );
		
		if ($client instanceof Zend_Gdata_HttpClient) {
			$filterResult = $client->filterHttpRequest ( 'GET', $request_uri );
			$url = $filterResult ['url'];
			$headers = $filterResult ['headers'];
			$client->setHeaders ( $headers );
			$client->setUri ( $url );
		} else {
			$client->setUri ( $request_uri );
		}
		
		try {
			$response = $client->request ( 'GET' );
		} catch ( Zend_Http_Client_Exception $e ) {
			require_once 'Zend/Gdata/App/HttpException.php';
			throw new Zend_Gdata_App_HttpException ( $e->getMessage (), $e );
		}
		
		// Parse Google's response
		if ($response->isSuccessful ()) {
			$goog_resp = array ();
			foreach ( explode ( "\n", $response->getBody () ) as $l ) {
				$l = chop ( $l );
				if ($l) {
					list ( $key, $val ) = explode ( '=', chop ( $l ), 2 );
					$goog_resp [$key] = $val;
				}
			}
			return $goog_resp ['Token'];
		} else {
			require_once 'Zend/Gdata/App/AuthException.php';
			throw new Zend_Gdata_App_AuthException ( 'Token upgrade failed. Reason: ' . $response->getBody () );
		}
	}
	
	/**
	 * Revoke a token
	 *
	 * @param string $token The token to revoke
	 * @param Zend_Http_Client $client (optional) HTTP client to use to make the request
	 * @param string $request_uri (optional) URI to which to direct the revokation request
	 * @return boolean Whether the revokation was successful
	 * @throws Zend_Gdata_App_HttpException
	 */
	public static function AuthSubRevokeToken($token, $client = null, $request_uri = self::AUTHSUB_REVOKE_TOKEN_URI) {
		$client = self::getHttpClient ( $token, $client );
		
		if ($client instanceof Zend_Gdata_HttpClient) {
			$filterResult = $client->filterHttpRequest ( 'GET', $request_uri );
			$url = $filterResult ['url'];
			$headers = $filterResult ['headers'];
			$client->setHeaders ( $headers );
			$client->setUri ( $url );
			$client->resetParameters ();
		} else {
			$client->setUri ( $request_uri );
		}
		
		ob_start ();
		try {
			$response = $client->request ( 'GET' );
		} catch ( Zend_Http_Client_Exception $e ) {
			ob_end_clean ();
			require_once 'Zend/Gdata/App/HttpException.php';
			throw new Zend_Gdata_App_HttpException ( $e->getMessage (), $e );
		}
		ob_end_clean ();
		// Parse Google's response
		if ($response->isSuccessful ()) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * get token information
	 *
	 * @param string $token The token to retrieve information about
	 * @param Zend_Http_Client $client (optional) HTTP client to use to
	 * make the request
	 * @param string $request_uri (optional) URI to which to direct
	 * the information request
	 */
	public static function getAuthSubTokenInfo($token, $client = null, $request_uri = self::AUTHSUB_TOKEN_INFO_URI) {
		$client = self::getHttpClient ( $token, $client );
		
		if ($client instanceof Zend_Gdata_HttpClient) {
			$filterResult = $client->filterHttpRequest ( 'GET', $request_uri );
			$url = $filterResult ['url'];
			$headers = $filterResult ['headers'];
			$client->setHeaders ( $headers );
			$client->setUri ( $url );
		} else {
			$client->setUri ( $request_uri );
		}
		
		ob_start ();
		try {
			$response = $client->request ( 'GET' );
		} catch ( Zend_Http_Client_Exception $e ) {
			ob_end_clean ();
			require_once 'Zend/Gdata/App/HttpException.php';
			throw new Zend_Gdata_App_HttpException ( $e->getMessage (), $e );
		}
		ob_end_clean ();
		return $response->getBody ();
	}
	
	/**
	 * Retrieve a HTTP client object with AuthSub credentials attached
	 * as the Authorization header
	 *
	 * @param string $token The token to retrieve information about
	 * @param Zend_Gdata_HttpClient $client (optional) HTTP client to use to make the request
	 */
	public static function getHttpClient($token, $client = null) {
		if ($client == null) {
			$client = new Zend_Gdata_HttpClient ();
		}
		if (! $client instanceof Zend_Gdata_HttpClient) {
			require_once 'Zend/Gdata/App/HttpException.php';
			throw new Zend_Gdata_App_HttpException ( 'Client is not an instance of Zend_Gdata_HttpClient.' );
		}
		$useragent = 'Zend_Framework_Gdata/' . Zend_Version::VERSION;
		$client->setConfig ( array ('strictredirects' => true, 'useragent' => $useragent ) );
		$client->setAuthSubToken ( $token );
		return $client;
	}

}
